using BootstrapBlazorApp.Server.LLMs.api;
using BootstrapBlazorApp.Server.LLMs.chain;
using BootstrapBlazorApp.Server.LLMs.function;
using BootstrapBlazorApp.Server.LLMs.messages;
using BootstrapBlazorApp.Server.LLMs.utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace BootstrapBlazorApp.Server.LLMs.model.TongYi;

public class ChatTongYi(TongYiConfig tongYiConfig) : IBaseModel
{
    private List<FunctionInput>? _functions;
    private Type? _toolType;

    public async Task<ChainContext> InvokeAsync(string input)
    {
        ChainContext chainContext = new ChainContext();
        chainContext.Add(new HumanMessage(input));
        return await ChatAsync(chainContext);
    }

    public async IAsyncEnumerable<ChainContext> InvokeStreamAsync(string input)
    {
        ChainContext chainContext = new ChainContext();
        chainContext.Add(new HumanMessage(input));
        await foreach (var value in ChatSteamAsync(chainContext))
        {
            yield return value;
        }
    }

    public void BindTools(Type type)
    {
        _functions = FunctionParserUtil.ParseFunction(type);
        _toolType = type;
    }


    public async Task<ChainContext> InvokeAsync(ChainContext input)
    {
        return await ChatAsync(input);
    }

    private async Task<ChainContext> ChatAsync(ChainContext input)
    {
        try
        {
            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }
            };
            string inputContent = BuildInput(input);
            string response =
                await TongYiUtils.SendPostRequestAsync(TongYiConfig.BaseUrl, inputContent, tongYiConfig.ApiKey);
            TongYiOutPut? outPut = JsonConvert.DeserializeObject<TongYiOutPut>(response, settings);
            outPut = await FunctionCall(input, settings,outPut);

            string? messageContent = outPut?.GetChoices().FirstOrDefault()?.ReturnMessage.Content;
            if (!string.IsNullOrEmpty(messageContent))
            {
                input.Add(new AiMessage(messageContent));
            }

            return input;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }

    
    //流式输出，不支持工具调用
    private async IAsyncEnumerable<ChainContext> ChatSteamAsync(ChainContext input)
    {
        string s = BuildInput(input,true);
        input.Add(new AiMessage(""));
        await foreach (var response in TongYiUtils.SendPostRequestStreamAsync(TongYiConfig.BaseUrl, s,
                           tongYiConfig.ApiKey))
        {
            if (response == null)
            {
                continue;
            }

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }
            };

            TongYiOutPut? outPut = JsonConvert.DeserializeObject<TongYiOutPut>(response, settings);
            var inputIncrementalOutput = outPut?.GetChoices().FirstOrDefault()?.ReturnMessage.Content;
            if (inputIncrementalOutput != null)
            {
                input.IncrementalOutput = inputIncrementalOutput;

                input.MessagesList.Last().Content += inputIncrementalOutput;
            }

            yield return input;
        }
    }

    public async IAsyncEnumerable<ChainContext> InvokeStreamAsync(ChainContext input)
    {
        await foreach (var chainValue in ChatSteamAsync(input))
        {
            yield return chainValue;
        }
    }

    //构建模型输入
    private string BuildInput(ChainContext input,bool isStream = false)
    {
        TongYiInput tongYiInput = new TongYiInput { Model = tongYiConfig.Model };
        if (isStream)
        {
            tongYiInput.Parameters.IncrementalOutput = true;
        }

        foreach (var baseMessage in input.MessagesList)
        {
            switch (baseMessage)
            {
                case ToolMessage toolMessage:
                    tongYiInput.Input.Messages.Add(new ReturnMessage
                    {
                        Role = toolMessage.Role, Content = toolMessage.Content, Name = toolMessage.Name
                    });
                    break;
                case AiMessage aiMessage:
                    tongYiInput.Input.Messages.Add(new ReturnMessage
                    {
                        Role = aiMessage.Role, Content = aiMessage.Content, ToolCalls = aiMessage.ToolCalls
                    });
                    break;
                default:
                    tongYiInput.Input.Messages.Add(new ReturnMessage
                    {
                        Role = baseMessage.Role, Content = baseMessage.Content
                    });
                    break;
            }
        }

        if (_functions != null)
        {
            tongYiInput.Parameters.Tools = _functions;
        }

        var setting = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            NullValueHandling = NullValueHandling.Ignore,
            Formatting = Formatting.Indented
        };
        return JsonConvert.SerializeObject(tongYiInput, setting);
    }
    //函数调用支持
    private async Task<TongYiOutPut?> FunctionCall(ChainContext input, JsonSerializerSettings settings,TongYiOutPut outPut)
    {
        string inputContent;
        string response;
        if (outPut.GetChoices().FirstOrDefault()?.ReturnMessage.ToolCalls == null || _toolType == null)
        {
            return outPut;
        }

        string functionOutPut;
        ReturnMessage returnMessage = outPut.GetChoices()[0].ReturnMessage;
        while (true)
        {
            try
            {
                functionOutPut = FunctionParserUtil.ExecuteTongYiFunction(_toolType, returnMessage)?.ToString() ??
                                 string.Empty;
                break;
            }
            catch
            {
                inputContent = BuildInput(input);
                response = await TongYiUtils.SendPostRequestAsync(TongYiConfig.BaseUrl, inputContent,
                    tongYiConfig.ApiKey);
                outPut = JsonConvert.DeserializeObject<TongYiOutPut>(response, settings) ?? throw new InvalidOperationException();
                returnMessage = outPut.GetChoices()[0].ReturnMessage;
            }
        }

        input.Add(new AiMessage(returnMessage.ToolCalls));
        string functionName = returnMessage.ToolCalls?.FirstOrDefault()?.Function.Name??"";
        input.Add(new ToolMessage(functionOutPut, functionName));
        inputContent = BuildInput(input);
        response = await TongYiUtils.SendPostRequestAsync(TongYiConfig.BaseUrl, inputContent,
            tongYiConfig.ApiKey);
        outPut = JsonConvert.DeserializeObject<TongYiOutPut>(response, settings)?? throw new InvalidOperationException();

        return outPut;
    }

}
